﻿using System;
using System.Linq;
using System.Reflection;
using NLite.Mini.Internal;
using NLite.Reflection;
using NLite.Internal;

namespace NLite.Mini.Listener
{
    /// <summary>
    /// 
    /// </summary>
    public class ComponentMemberRegisterListner:ComponentListenerAdapter
    {
        /// <summary>
        /// 
        /// </summary>
        public ComponentMemberRegisterListner() : base(ComponentListenStage.PostInitialization | ComponentListenStage.PreDestroy) { }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="ctx"></param>
        public override void OnPostInitialization(NLite.Mini.Context.IPostCreationContext ctx)
        {
            if (ctx.Component.Implementation == null
                || ctx.Instance == null)
                return;

            Lazy<MemberModel[]> lazy = null;
            if (ctx.Component.ExtendedProperties.Contains("MembersRegistered"))
                lazy = ctx.Component.ExtendedProperties["MembersRegistered"] as Lazy<MemberModel[]>;

            if (lazy == null)
            {
                const BindingFlags Flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;

                lazy = new Lazy<MemberModel[]>(() =>
                    {
                        var instanceType = ctx.Component.Implementation;
                        return (from item in instanceType.GetFields(Flags)
                                     let attr = item.GetAttribute<ComponentAttribute>(true)
                                     let id = attr != null ? attr.Id : null
                                     where attr != null
                                select new MemberModel { Id = id,  Contract= attr.Contract, MemberType = item.FieldType, Member = item, Getter = DynamicMethodFactory.GetGetter(item) })
                                    .Union(
                                    from item in instanceType.GetProperties(Flags)
                                    let attr = item.GetAttribute<ComponentAttribute>(true)
                                    let id = attr != null ? attr.Id : null
                                    let ps = item.GetIndexParameters()
                                    where attr != null && (ps == null || ps.Length == 0)
                                    select new MemberModel { Id = id, Contract = attr.Contract, MemberType = item.PropertyType, Member = item, Getter = DynamicMethodFactory.GetGetter(item) })
                                     .Union(
                                    from item in instanceType.GetMethods(Flags)
                                    let attr = item.GetAttribute<ComponentAttribute>(true)
                                    let id = attr != null ? attr.Id : Guid.NewGuid().ToString()
                                    let ps = item.GetParameters()
                                    where attr != null && (ps == null || ps.Length == 0) && item.ReturnType != Types.Void
                                    select new MemberModel { Id = id, Contract = attr.Contract, MemberType = item.ReturnType, Member = item, Getter = DynamicMethodFactory.GetGetter(item) })
                                    .ToArray();
                    });

                ctx.Component.ExtendedProperties["MembersRegistered"] = lazy;
            }

          

            if (lazy.Value.Length == 0)
                return;

            foreach (var item in lazy.Value)
            {
                if (item.Contract != null &&  item.Contract.IsSubclassOf(Types.Delegate))
                    ctx.Kernel.RegisterInstance(item.Id, item.Contract, Delegate.CreateDelegate(item.Contract, ctx.Instance, item.Member as MethodInfo));
                else
                    ctx.Kernel.RegisterInstance(item.Id, item.MemberType, item.Getter(ctx.Instance));
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="info"></param>
        /// <param name="instance"></param>
        public override void OnPreDestroy(IComponentInfo info, object instance)
        {
            if (info.ExtendedProperties.Contains("MembersRegistered"))
            {
                var lazy = info.ExtendedProperties["MembersRegistered"] as Lazy<MemberModel[]>;
                foreach (var item in lazy.Value)
                    ServiceRegistry.Current.UnRegister(item.Id);
            }
        }
    }
}
